#include "cfg_visitor.hpp"

CFGvisitor::CFGvisitor() {
	this->runid = this->this_number()+1;
}
void CFGvisitor::visit(box *n) {}
void CFGvisitor::visit(diamond *n) {}

void CFGvisitor::visit_vertex(CFGvisitable* n) {
	if (!n) return;

	if (n->visited != runid) { // Kun besøg, hvis ikke den er besøgt i forvejen
		n->visited = runid;
		n->acceptVisit(this); // Besøg
	}
}

////////////////////////////////////////////////////////////////////////////////


CFGvisitorDOT::CFGvisitorDOT() {
	this->graf = new dot();
}

void CFGvisitorDOT::writeDOTfile(const char *fPath) {
	return graf->writeDOTfile(fPath);
}

void CFGvisitorDOT::StartVisit(CFGvertex *n) {
	visit_vertex(n);
}

void CFGvisitorDOT::visit(box *n) {
	ASTvisitorString hej;
	string label = hej.convert(n->payload);
	label = string("[") + label + "]" + ::stringify(n->label());
	
	CFGvertex *dest = n->child.getDestination();
	
	graf->addNode(n).Set("label", label);
	if (dest) graf->addKant(n, dest);	// We need the nullptr check since n might be an extremal vertex
	
	visit_vertex(n->child.getDestination());
}

void CFGvisitorDOT::visit(diamond *n) {
	ASTvisitorString hej;
	string label = hej.convert(n->condition);
	label = string("[") + label + "]" + ::stringify(n->label());
	
	CFGvertex *tdest = n->true_branch.getDestination();
	CFGvertex *fdest = n->false_branch.getDestination();

	assert(tdest && "True branch must have a destination");
	assert(fdest && "False branch must have a destination");

	graf->addNode(n).Set("label", label).Set("shape", "diamond");
	graf->addKant(n, tdest).Set("label", "true");
	graf->addKant(n, fdest).Set("label", "false");
	
	visit_vertex(n->true_branch.getDestination());	
	visit_vertex(n->false_branch.getDestination());	
}

////////////////////////////////////////////////////////////////////////////////

void CFGvisitorFlatInfo::StartVisit(CFGvertex *n) {
	visit_vertex(n);
}

void CFGvisitorFlatInfo::visit(box *n) {
	this->add_to_labels(n);
	this->add_to_flows(n->child);
	this->handle_extremal(n->child);
	if (dynamic_cast<bexp*>(n->payload) == NULL)
		this->reverse_payload[n->payload] = n;	// predicates do not own their condition
	
	visit_vertex(n->child.getDestination());
}

void CFGvisitorFlatInfo::visit(diamond *n) {
	this->add_to_labels(n);
	this->add_to_flows(n->false_branch);
	this->add_to_flows(n->true_branch);
	this->handle_extremal(n->true_branch);
	this->handle_extremal(n->false_branch);
	this->reverse_payload[n->condition] = n;
	
	visit_vertex(n->true_branch.getDestination());	
	visit_vertex(n->false_branch.getDestination());	
}


void CFGvisitorFlatInfo::add_to_labels(CFGvertex *n) {
	if (this->labels.size() <= n->label())
		this->labels.resize(n->label()+1);
	this->labels[n->label()] = n;
}

void CFGvisitorFlatInfo::add_to_flows(const CFGedge& e) {
	assert(e.getSource());
	if (e.getDestination() == NULL) return;	// Don't try to add an egde which does not exist
	
	this->flow.push_back(make_pair(e.getSource()->label(), e.getDestination()->label()));
	this->flowR.push_back(make_pair(e.getDestination()->label(), e.getSource()->label()));
}

void CFGvisitorFlatInfo::handle_extremal(const CFGedge& e) {
	assert(e.getSource() != NULL);
	CFGvertex *n = e.getSource();
	
	if (e.getDestination() == NULL) {
		this->final.insert(n->label()); // n has no outgoing edges, so it must belong to final
	}
	
	if (n->incoming_edges().size() == 0) {
		this->init = n->label();
	}
}
